﻿namespace DataIntegration.Extracters
{
    using System;
    using System.Data;
    using System.Reflection;
    using Entities;
    using Entities.Data;
    using Entities.Triggers;
    using log4net;
    using Messaging.Publisher;
    using Storage;
    using Utils;
    using Utils.Lifecycle.Base;
    using Utils.Serialization;

    public abstract class Extracter : BaseLifecycle, IBrokerComponent
    {
        private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        public string DaemonName { get; set; }

        protected abstract void Extract(TriggerInstruction triggerInstruction);

        protected readonly string name;

        private readonly IPublisherFactory publisherFactory;

        private readonly IDataStoreFactory dataStoreFactory;

        private readonly string filter;

        private IPublisher publisher;

        private IDataStore dataStore;

        protected Extracter(string name, IPublisherFactory publisherFactory, IDataStoreFactory dataStoreFactory, string filter)
        {
            Logger.DebugFormat("Creating Extracter with name=[{0}] filter=[{1}]", name, filter);
            this.name = name;
            this.publisherFactory = publisherFactory;
            this.dataStoreFactory = dataStoreFactory;
            this.filter = filter;
        }

        public override void Dispose()
        {
            Logger.DebugFormat("Disposing [{0}]", this.name);
            this.Dispose(this.publisher);
        }

        protected override void Setup()
        {
            Logger.DebugFormat("Setting up [{0}]", this.name);
            this.publisher = this.publisherFactory.GetPublisher(this.DaemonName, MessageType.Data, this.name);
            this.publisher.Start();
            this.dataStore = this.dataStoreFactory.GetDataStore();
        }

        protected void Commit(DataTable dataTable)
        {
            Logger.DebugFormat("Commiting datatable [{0}] to storage", dataTable.TableName);
            this.dataStore.Commit(dataTable);
        }

        protected void ProcessTrigger(string messageId, TriggerInstruction triggerInstruction)
        {
            Logger.DebugFormat("Processing trigger for target [{0}]", triggerInstruction);

            try
            {
                this.Extract(triggerInstruction);
                DataTable filteredDataTable = this.dataStore.Load(this.name, this.filter);
                ExtractedData extractedData = new ExtractedData(filteredDataTable);
                string xml = SerializationFactory.SerializeToXmlString(extractedData);
                string topic = string.Format("{0}.{1}.{2}", App.Name, MessageType.Data, this.name);
                this.publisher.Publish(topic, messageId, xml);
                this.dataStore.Clear(this.name);
            }
            catch (Exception exception)
            {
                Logger.Error("Processing of triggered event failed", exception);    
            }
        }
    }
}